#include "objParser.h"

using namespace std;

bool MeshUtils::ObjParser::parseNode(std::ifstream& source, double& x, double& y, double& z)
{
	Lexeme lex = ParserHelper::lexer(source);
	if(lex.type != INTEGER and lex.type != REAL)
		throw ParserException("Failed to read x coord");
	if(lex.type == INTEGER)
		x = (double) lex.integer;
	else
		x = (double) lex.real;

	lex = ParserHelper::lexer(source);
	if(lex.type != INTEGER and lex.type != REAL)
		throw ParserException("Failed to read y coord");
	if(lex.type == INTEGER)
		y = (double) lex.integer;
	else
		y = lex.real;

	lex = ParserHelper::lexer(source);
	if(lex.type != INTEGER and lex.type != REAL)
		throw ParserException("Failed to read z coord");
	if(lex.type == INTEGER)
		z = (double) lex.integer;
	else
		z = (double) lex.real;

	lex = ParserHelper::lexer(source);
	if(lex.type != EOL)
		throw ParserException("Failed to read eol");

	return true;
}

bool MeshUtils::ObjParser::parseFace(std::ifstream& source, unsigned int& A, unsigned int& B, unsigned int& C, unsigned int& nA, unsigned int& nB, unsigned int& nC, unsigned int& tA, unsigned int& tB, unsigned int& tC)
{
	Lexeme lex = ParserHelper::lexer(source);
	if(lex.type != INTEGER)
		throw ParserException("Failed to read face first node index");
	A = lex.integer-1;

	lex = ParserHelper::lexer(source);
	if(lex.type == SEPARATOR)
	{
		lex = ParserHelper::lexer(source);
		if(lex.type == SEPARATOR)
		{
			lex = ParserHelper::lexer(source);
			if(lex.type != INTEGER)
				throw ParserException("Failed to read face first node normal index");
			nA = lex.integer-1;
			lex = ParserHelper::lexer(source);
		}
		else 
		{
			if(lex.type != INTEGER)
				throw ParserException("Failed to read face first node texture index");
			tA = lex.integer-1;
			lex = ParserHelper::lexer(source);
			if(lex.type == SEPARATOR)
			{
				lex = ParserHelper::lexer(source);
				if(lex.type != INTEGER)
					throw ParserException("Failed to read face first node normal index");
				nA = lex.integer-1;
				lex = ParserHelper::lexer(source);
			}
		}
		
	}
	else if(lex.type != INTEGER)
		throw ParserException("Failed to read face : invalid number of nodes in nodesList");
	B = lex.integer-1;

	lex = ParserHelper::lexer(source);
	if(lex.type == SEPARATOR)
	{
		lex = ParserHelper::lexer(source);
		if(lex.type == SEPARATOR)
		{
			lex = ParserHelper::lexer(source);
			if(lex.type != INTEGER)
				throw ParserException("Failed to read face first node normal index");
			nB = lex.integer-1;
			lex = ParserHelper::lexer(source);
		}
		else 
		{
			if(lex.type != INTEGER)
				throw ParserException("Failed to read face first node texture index");
			tB = lex.integer-1;
			lex = ParserHelper::lexer(source);
			if(lex.type == SEPARATOR)
			{
				lex = ParserHelper::lexer(source);
				if(lex.type != INTEGER)
					throw ParserException("Failed to read face first node normal index");
				nB = lex.integer-1;
				lex = ParserHelper::lexer(source);
			}
		}
	}
	else if(lex.type != INTEGER)
		throw ParserException("Failed to read face : invalid number of nodes in nodesList");
	C = lex.integer-1;

	lex = ParserHelper::lexer(source);
	if(lex.type == SEPARATOR)
	{
		lex = ParserHelper::lexer(source);
		if(lex.type == SEPARATOR)
		{
			lex = ParserHelper::lexer(source);
			if(lex.type != INTEGER)
				throw ParserException("Failed to read face first node normal index");
			nC = lex.integer-1;
			lex = ParserHelper::lexer(source);
		}
		else 
		{
			if(lex.type != INTEGER)
				throw ParserException("Failed to read face first node texture index");
			tC = lex.integer-1;
			lex = ParserHelper::lexer(source);
			if(lex.type == SEPARATOR)
			{
				lex = ParserHelper::lexer(source);
				if(lex.type != INTEGER)
					throw ParserException("Failed to read face first node normal index");
				nC = lex.integer-1;
				lex = ParserHelper::lexer(source);
			}
		}
	}
	else if(lex.type != EOL)
		throw ParserException("Failed to eol");

	return true;
}

void MeshUtils::ObjParser::load(std::vector<MeshUtils::Vector3>& nodes, std::vector<MeshUtils::Vector3>& normals,std::vector<MeshUtils::Vector3>& textures,  std::vector<MeshUtils::Face_t>& faces,  unsigned int& nbNodes, unsigned int& nbNorms, unsigned int& nbFaces)
{
	std::ifstream infile(filename);

	try {
		while(infile.good())
		{
			Lexeme lex = ParserHelper::lexer(infile);

			if(lex.type == COMMENT)
			{
				continue;
			}
			if(lex.type == EOL)
			{
				continue;
			}
			if(lex.type == ALPHA )
			{
				if(string(lex.alpha).compare("v") == 0)
				{
					MeshUtils::Vector3 node;
					parseNode(infile, node.x, node.y, node.z);
					nodes.push_back(node);
				}

				if(string(lex.alpha).compare("vn") == 0)
				{
					MeshUtils::Vector3 normal;
					parseNode(infile, normal.x, normal.y, normal.z);
					normal.x *= -1.0;
					normal.y *= -1.0;
					normal.z *= -1.0;
					
					normals.push_back(normal);
				}

				if(string(lex.alpha).compare("vt") == 0)
				{
					MeshUtils::Vector3 texture;
					parseNode(infile, texture.x, texture.y, texture.z);

					textures.push_back(texture);
				}

				if(string(lex.alpha).compare("f") == 0)
				{
					MeshUtils::Face_t face;
					parseFace(infile, face.A, face.B, face.C, face.normalA, face.normalB, face.normalC, face.textA, face.textB, face.textC);
					unsigned int swap = face.B;
					face.B = face.C;
					face.C = swap;

					

					swap = face.normalB;
					face.normalB = face.normalC;
					face.normalC = swap;

					swap = face.textB;
					face.textB = face.textC;
					face.textC = swap;
					faces.push_back(face);
				}
			}
		}
		nbNodes = nodes.size();
		nbNorms = normals.size();
		nbFaces = faces.size();
		std::cout << "end parsing faces : " << faces.size() << " faces (counted) " << endl;
		std::cout << "end parsing nodes : " << nodes.size() << " nodes (counted) " << endl;
		std::cout << "end parsing normals : " << normals.size() << " normals (counted) " << endl;
		std::cout << "end parsing texture coord : " << textures.size() << "textures coord (counted)" << endl;
 	} catch(ParserException e)
	{
		infile.close();
		std::cout << e.what() << std::endl;
		//exit(0);
	}
	
	infile.close();
	return;
}
